package com.google.code.mergeinformer.controller;

import com.google.code.mergeinformer.model.Project;
import com.google.code.mergeinformer.model.Task;
import com.google.code.mergeinformer.util.Measured;
import com.google.code.mergeinformer.util.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import java.util.Collections;

import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;

/**
 * @author Dmitry Golubev
 */
@Controller
public class TaskController extends ControllerBase {

    private static final String EDIT_TASK_VIEW = "editTask";

    @RequestMapping("/{projectShortName:.+}/{taskShortName:.+}")
    public String showTask(@PathVariable String projectShortName, @PathVariable String taskShortName) {
        final Project currentProject = projectService.getByShortName(projectShortName);
        if (currentProject == null) {
            return getFallback();
        }

        final Task currentTask = taskService.getByShortName(projectShortName, taskShortName);
        if (currentTask == null) {
            return getRedirect(currentProject);
        }

        setCurrent(currentProject, currentTask);

        return HomeController.HOME_VIEW;
    }

    @RequestMapping(value = "/{projectShortName:.+}/{taskShortName:.+}", params = "execute")
    public String executeTask(@PathVariable String projectShortName, @PathVariable String taskShortName) {
        final Project currentProject = projectService.getByShortName(projectShortName);
        if (currentProject == null) {
            return getFallback();
        }

        final Task currentTask = taskService.getByShortName(projectShortName, taskShortName);
        if (currentTask == null) {
            return getRedirect(currentProject);
        }

        setCurrent(currentProject, currentTask);

        final long elapsed = new Measured() {
            @Override
            protected void measuredBlock() {
                taskService.execute(currentTask);
            }
        }.execute();

        final Notification notification;
        if (currentTask.getStackTrace() == null) {
            notification = buildNotification(
                    Notification.Type.success,
                    "Update task",
                    "Task <b>" + currentTask.getName() + "</b> was successfully updated",
                    elapsed
            );
        } else {
            notification = buildNotification(
                    Notification.Type.error,
                    "Update task",
                    "Task <b>" + currentTask.getName() + "</b> was not updated, see stacktrace for details",
                    elapsed
            );
        }
        pushNotifications(notification);

        return getRedirect(currentProject, currentTask);
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "new-task")
    public String addTask(@PathVariable String projectShortName) {
        final Project currentProject = projectService.getByShortName(projectShortName);
        if (currentProject == null) {
            return getFallback();
        }
        Task currentTask = null;
        if (isCurrentProject(projectShortName)) {
            final String taskShortName = getCurrentFromSession().getTaskShortName();
            if (taskShortName != null) {
                currentTask = taskService.getByShortName(projectShortName, taskShortName);
            }
        }
        setCurrent(currentProject, currentTask);

        Collections.sort(currentProject.getBranches());

        return EDIT_TASK_VIEW;
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "create")
    public @ResponseBody ValidationResult createTask(@PathVariable String projectShortName, @RequestBody Task task) {
        final ValidationResult result = validate(task);
        if (result.isValid()) {
            final Project project = projectService.getByShortName(projectShortName);
            if (project == null) {
                throw new IllegalArgumentException("Project shortName=" + projectShortName + " was not found");
            }
            project.addTask(task);
            projectService.persist(project);
            taskService.schedule(task);

            result.setData(task.getShortName());

            pushNotifications(new Notification(
                    Notification.Type.success,
                    "New task",
                    "Task <b>" + task.getName() + "</b> in project <b>" + project.getName() + "</b> was successfully created"
            ));
        }
        return result;
    }

    @RequestMapping(value = "/{projectShortName:.+}/{taskShortName:.+}", params = "edit")
    public String editTask(@PathVariable String projectShortName, @PathVariable String taskShortName, Model model) {
        final Project currentProject = projectService.getByShortName(projectShortName);
        if (currentProject == null) {
            return getFallback();
        }

        final Task currentTask = taskService.getByShortName(projectShortName, taskShortName);
        if (currentTask == null) {
            return getRedirect(currentProject);
        }

        setCurrent(currentProject, currentTask);
        model.addAttribute("edit", Boolean.TRUE);

        Collections.sort(currentProject.getBranches());

        return EDIT_TASK_VIEW;
    }

    @RequestMapping(value = "/{projectShortName:.+}/{taskShortName:.+}", params = "apply")
    public @ResponseBody ValidationResult saveChanges(@PathVariable String projectShortName, @PathVariable String taskShortName, @RequestBody Task editedTask) {
        if (!projectService.isShortNameExists(projectShortName)) {
            throw new IllegalArgumentException("Project shortName=" + projectShortName + " was not found");
        }
        final Task task = taskService.getByShortName(projectShortName, taskShortName);
        if (task == null) {
            throw new IllegalArgumentException("Task shortName=" + taskShortName + " was not found");
        }

        final ValidationResult result = validate(editedTask);
        if (result.isValid()) {
            final boolean mustReschedule = !task.getExecutionInterval().equals(editedTask.getExecutionInterval());
            task.copyAttributes(editedTask);
            taskService.persist(task);
            if (mustReschedule) {
                taskService.schedule(task);
            }
            result.setData(task.getShortName());

            pushNotifications(new Notification(
                    Notification.Type.success,
                    "Edit task",
                    "Task <b>" + task.getName() + "</b> was successfully edited"));
        }
        return result;
    }

    @RequestMapping(value = "/{projectShortName:.+}/{taskShortName:.+}", params = "delete")
    public String deleteTask(@PathVariable String projectShortName, @PathVariable String taskShortName) {
        final Task task = taskService.getByShortName(projectShortName, taskShortName);
        if (task == null) {
            if (!projectService.isShortNameExists(projectShortName)) {
                return getFallback();
            }
            return getRedirect(projectShortName);
        }

        taskService.remove(task);

        final Project project = projectService.getByShortName(projectShortName);

        pushNotifications(new Notification(
                Notification.Type.success,
                "Delete task",
                "Task <b>" + task.getName() + "</b> of project <b>" + project.getName() + "</b> was successfully deleted"
        ));

        if (isCurrentTask(projectShortName, taskShortName)) {
            return getRedirect(projectShortName);
        } else if (isCurrentProject(projectShortName)) {
            return getRedirect(projectShortName, getCurrentFromSession().getTaskShortName());
        }

        return getRedirect(projectShortName, taskShortName);
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "is-short-name-available", produces = APPLICATION_JSON_VALUE)
    public @ResponseBody Boolean isTaskShortNameAvailable(@PathVariable String projectShortName, @RequestParam String shortName) {
        return !taskService.isShortNameExists(StringUtils.normalize(projectShortName), StringUtils.normalize(shortName));
    }

    @RequestMapping(value = "/{projectShortName:.+}", params = "is-name-available", produces = APPLICATION_JSON_VALUE)
    public @ResponseBody Boolean isTaskNameAvailable(@PathVariable String projectShortName, @RequestParam String name) {
        return !taskService.isNameExists(StringUtils.normalize(projectShortName), StringUtils.normalize(name));
    }
}
